package com.qudian.pay.core.diagnostic;

import java.util.Map;
import org.apache.commons.lang3.ObjectUtils;
import com.google.common.collect.Maps;

/**
 * @Description: 性能诊断工具
 * @ClassName: Profiler
 * @author gaoxiang
 * @date 2015年11月17日 下午10:39:30
 */
public final class Profiler {
	
	/**
	 * @Fields entryStack : 线程变量->方法抽象类
	 */ 
	private static final ThreadLocal<Entry> entryStack = new ThreadLocal<Entry>();
	
	/**
	 * @Fields profilerContext : 线程变量->方法调用栈深
	 */ 
	private static ThreadLocal<Integer> stackDepthContext = new ThreadLocal<Integer>() {
		public Integer initialValue() {
			return 0;
		}
	};
	
	/**
	 * @Fields invokeTimeContext : 线程变量->针对大循环的
	 */ 
	private static ThreadLocal<Map<String,Integer>> invokeTimeContext = new ThreadLocal<Map<String,Integer>>() {
		// 初始化值
		public Map<String,Integer> initialValue() {
			return Maps.newHashMap();
		}
	};
	
	/**
	 * @Description: 该线程栈开始调用方法
	 * @param message
	 * @return void 返回类型
	 * @author gaoxiang
	 * @date 2016年5月5日 上午11:47:42
	 */
	public static void start(String message) {
		// 如果是在方法内，则enter成分支
		if (isStart()) {
			enter(message);
		} else {
			//方法入口
			entryStack.set(new Entry(message, null, null));
		}
		//栈深+1
		stackDepthContext.set(stackDepthContext.get() + 1);
	}
	
	/**
	 * @Description: 该线程栈是否已经开启统计
	 * @return boolean 返回类型
	 * @author gaoxiang
	 * @date 2016年5月5日 上午11:50:00
	 */
	public static boolean isStart() {
		return null != getEntry() && getEntry().isStart();
	}
	
	/**
	 * @Description: 重置线程栈上下文(使用不当会造成内存泄漏)
	 * @return void 返回类型
	 * @author gaoxiang
	 * @date 2016年5月5日 上午11:52:11
	 */
	public static void reset() {
		Integer stackDepth = stackDepthContext.get();
		if (null != stackDepth && stackDepth > 1) {
			//栈深-1
			stackDepthContext.set(stackDepth - 1);
		} else {
			entryStack.set(null);
			stackDepthContext.set(0);
			invokeTimeContext.get().clear();
		}
	}
	
	/**
	 * @Description: 子方法性能统计
	 * @author gaoxiang
	 * @date 2017年4月1日 下午3:48:04
	 */
	public static void enter(String message) {
		Entry currentEntry = getCurrentEntry();
		if (currentEntry != null) {
			String key = currentEntry.hashCode()+message;
			Integer times = ObjectUtils.defaultIfNull(invokeTimeContext.get().get(key), 1);
			//同一方法超过 threshold次 认为是循环调用
			if(times < Entry.threshold) {
				currentEntry.enterSubEntry(message);
			} else {
				currentEntry.enterSubEntry(message,times);
			}
			invokeTimeContext.get().put(key, ++times);
		}
	}
	
	public static void release(String message) {
		Entry currentEntry = getCurrentEntry();
		if (currentEntry != null) {
			String key = currentEntry.hashCode()+message;
			Integer times = ObjectUtils.defaultIfNull(invokeTimeContext.get().get(key), 1);
			//同一方法超过 threshold次 认为是循环调用
			if(times < Entry.threshold) {
				currentEntry.release();
			} else {
			}
		}
	}
	
	public static void release() {
		Entry currentEntry = getCurrentEntry();
		if (currentEntry != null) {
			currentEntry.release();
		}
	}
	
	public static long getDuration() {
		Entry entry = (Entry) entryStack.get();
		if (entry != null) {
			return entry.getDuration();
		} else {
			return -1;
		}
	}
	
	public static String dump() {
		return dump("", "");
	}
	
	/**
	 * 判断被父级方法调用过
	 */
	public static boolean isSuperStart() {
		return stackDepthContext.get() > 1;
	}
	
	public static String dump(String prefix) {
		return dump(prefix, prefix);
	}
	
	public static String dump(String prefix1, String prefix2) {
		Entry entry = (Entry) entryStack.get();
		if (entry != null) {
			return entry.toString(prefix1, prefix2);
		} else {
			return "";
		}
	}
	
	public static Entry getEntry() {
		return (Entry) entryStack.get();
	}
	
	private static Entry getCurrentEntry() {
		Entry subEntry = (Entry) entryStack.get();
		Entry entry = null;
		if (subEntry != null) {
			do {
				entry = subEntry;
				subEntry = entry.getUnreleasedEntry();
			} while (subEntry != null);
		}
		return entry;
	}
	
}
